{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nibpbUnTsxTd"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "tXAbWHtqs1Y2"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HTgMAvQq-PU_"
      },
      "source": [
        "# 不規則なテンソル\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/ragged_tensor\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/ragged_tensor.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/ragged_tensor.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/ragged_tensor.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5DP8XNP-6zlu"
      },
      "source": [
        "**APIドキュメント：** [`tf.RaggedTensor`](https://www.tensorflow.org/api_docs/python/tf/ragged) [`tf.Ragged`](https://www.tensorflow.org/api_docs/python/tf/RaggedTensor)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cDIUjj07-rQg"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KKvdSorS-pDD"
      },
      "outputs": [],
      "source": [
        "!pip install -q tf_nightly\n",
        "import math\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pxi0m_yf-te5"
      },
      "source": [
        "## 概要\n",
        "\n",
        "データにはさまざまな形状があります。テンソルも同じです。*不規則なテンソル*は、TensorFlow 相当のネストされた可変長リストです。このテンソルを使用すると、次のような不均一な形状のデータを簡単に保存して処理できます。\n",
        "\n",
        "- 映画の俳優のセットなど、可変長の特徴量。\n",
        "- テキストや動画クリップなど、可変長シーケンシャル入力のバッチ。\n",
        "- セクション、段落、文、単語に細分されたテキストドキュメントなどの階層的な入力。\n",
        "- プロトコルバッファなどの構造化入力の個々のフィールド。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1mhU_qY3_mla"
      },
      "source": [
        "### 不規則なテンソルを使用してできること\n",
        "\n",
        "不規則なテンソルは、100 個以上の TensorFlow 演算（`tf.add` や `tf.reduce_mean` など）、配列演算（`tf.concat` や `tf.tile` など）、文字列操作演算（`tf.substr` など）、制御フロー演算（`tf.while_loop` や `tf.map_fn` など）、およびその他多数の演算でサポートされています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vGmJGSf_-PVB"
      },
      "outputs": [],
      "source": [
        "digits = tf.ragged.constant([[3, 1, 4, 1], [], [5, 9, 2], [6], []])\n",
        "words = tf.ragged.constant([[\"So\", \"long\"], [\"thanks\", \"for\", \"all\", \"the\", \"fish\"]])\n",
        "print(tf.add(digits, 3))\n",
        "print(tf.reduce_mean(digits, axis=1))\n",
        "print(tf.concat([digits, [[5, 3]]], axis=0))\n",
        "print(tf.tile(digits, [1, 2]))\n",
        "print(tf.strings.substr(words, 0, 2))\n",
        "print(tf.map_fn(tf.math.square, digits))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Pt-5OIc8-PVG"
      },
      "source": [
        "ファクトリメソッド、変換メソッド、および値のマッピング演算など、不規則なテンソルに固有のメソッドや演算も多数あります。サポートされている演算のリストについては、**`tf.ragged` パッケージドキュメント**をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "r8fjGgf3B_6z"
      },
      "source": [
        "不規則なテンソルは、[Keras](https://www.tensorflow.org/guide/keras)、[Datasets](https://www.tensorflow.org/guide/data)、[tf.function](https://www.tensorflow.org/guide/function)、[SavedModels](https://www.tensorflow.org/guide/saved_model)、および [tf.Example](https://www.tensorflow.org/tutorials/load_data/tfrecord) など、多数の TensorFlow API でサポートされています。詳細については、以下の「**TensorFlow API**」のセクションをご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aTXLjQlcHP8a"
      },
      "source": [
        "通常のテンソルと同様に、Python スタイルのインデキシングを使用して、不規則なテンソルの特定のスライスにアクセスできます。詳細については、以下の「**インデキシング**」のセクションをご覧ください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "n8YMKXpI-PVH"
      },
      "outputs": [],
      "source": [
        "print(digits[0])       # First row"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Awi8i9q5_DuX"
      },
      "outputs": [],
      "source": [
        "print(digits[:, :2])   # First two values in each row."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sXgQtTcgHHMR"
      },
      "outputs": [],
      "source": [
        "print(digits[:, -2:])  # Last two values in each row."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6FU5T_-8-PVK"
      },
      "source": [
        "また、通常のテンソルと同様に、Python の算術演算子と比較演算子を使用して、要素ごとの演算を実行できます。詳細については、以下の「**オーバーロードされた演算子**」をご覧ください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2tdUEtb7-PVL"
      },
      "outputs": [],
      "source": [
        "print(digits + 3)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "X-bxG0nc_Nmf"
      },
      "outputs": [],
      "source": [
        "print(digits + tf.ragged.constant([[1, 2, 3, 4], [], [5, 6, 7], [8], []]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2tsw8mN0ESIT"
      },
      "source": [
        "`RaggedTensor` の値に要素ごとの変換を実行する必要がある場合は、 `tf.ragged.map_flat_values` を使用できます。これは、関数と 1 つ以上の引数をとり、関数を適用して`RaggedTensor` の値を変換します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pvt5URbdEt-D"
      },
      "outputs": [],
      "source": [
        "times_two_plus_one = lambda x: x * 2 + 1\n",
        "print(tf.ragged.map_flat_values(times_two_plus_one, digits))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HNxF6_QKAzkl"
      },
      "source": [
        "不規則なテンソルは、ネストされた Python `list` と numpy の `array` に変換できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "A5NHb8ViA9dt"
      },
      "outputs": [],
      "source": [
        "digits.to_list()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2o1wogVyA6Yp"
      },
      "outputs": [],
      "source": [
        "digits.numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7M5RHOgp-PVN"
      },
      "source": [
        "### 不規則なテンソルを構築する\n",
        "\n",
        "不規則なテンソルを構築するには、`tf.ragged.constant` を使用するのが最も簡単な方法です。これは、特定のネストされた Python `list` または numpy の `array` に応じた `RaggedTensor` を構築します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yhgKMozw-PVP"
      },
      "outputs": [],
      "source": [
        "sentences = tf.ragged.constant([\n",
        "    [\"Let's\", \"build\", \"some\", \"ragged\", \"tensors\", \"!\"],\n",
        "    [\"We\", \"can\", \"use\", \"tf.ragged.constant\", \".\"]])\n",
        "print(sentences)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TW1g7eE2ee8M"
      },
      "outputs": [],
      "source": [
        "paragraphs = tf.ragged.constant([\n",
        "    [['I', 'have', 'a', 'cat'], ['His', 'name', 'is', 'Mat']],\n",
        "    [['Do', 'you', 'want', 'to', 'come', 'visit'], [\"I'm\", 'free', 'tomorrow']],\n",
        "])\n",
        "print(paragraphs)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SPLn5xHn-PVR"
      },
      "source": [
        "不規則なテンソルは、フラット*値*のテンソルと*行を分割する*テンソルを組み合わせ、`tf.RaggedTensor.from_value_rowids`、`tf.RaggedTensor.from_row_lengths`、および`tf.RaggedTensor.from_row_splits` などのファクトリクラスメソッドを使ってこれらの値をどのように行に分割するのかを示すことで、構築することもできます。\n",
        "\n",
        "#### `tf.RaggedTensor.from_value_rowids`\n",
        "\n",
        "各値が属する行がわかっている場合は、`value_rowids` 行分割テンソルを使用して、`RaggedTensor` を構築できます。\n",
        "\n",
        "![value_rowids](https://www.tensorflow.org/images/ragged_tensors/value_rowids.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SEvcPUcl-PVS"
      },
      "outputs": [],
      "source": [
        "print(tf.RaggedTensor.from_value_rowids(\n",
        "    values=[3, 1, 4, 1, 5, 9, 2],\n",
        "    value_rowids=[0, 0, 0, 0, 2, 2, 3]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RBQh8sYc-PVV"
      },
      "source": [
        "#### `tf.RaggedTensor.from_row_lengths`\n",
        "\n",
        "各行の長さがわかっている場合は、`row_lengths` 行分割テンソルを使用できます。\n",
        "\n",
        "![row_lengths](https://www.tensorflow.org/images/ragged_tensors/row_lengths.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LBY81WXl-PVW"
      },
      "outputs": [],
      "source": [
        "print(tf.RaggedTensor.from_row_lengths(\n",
        "    values=[3, 1, 4, 1, 5, 9, 2],\n",
        "    row_lengths=[4, 0, 2, 1]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8p5V8_Iu-PVa"
      },
      "source": [
        "#### `tf.RaggedTensor.from_row_splits`\n",
        "\n",
        "各行が開始および終了するインデックスがわかっている場合は、`row_splits` 行分割テンソルを使用できます。\n",
        "\n",
        "![row_splits](https://www.tensorflow.org/images/ragged_tensors/row_splits.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FwizuqZI-PVb"
      },
      "outputs": [],
      "source": [
        "print(tf.RaggedTensor.from_row_splits(\n",
        "    values=[3, 1, 4, 1, 5, 9, 2],\n",
        "    row_splits=[0, 4, 4, 6, 7]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E-9imo8DhwuA"
      },
      "source": [
        "ファクトリメソッドの全リストについては、`tf.RaggedTensor` クラスのドキュメントをご覧ください。\n",
        "\n",
        "注意: デフォルトでは、これらのファクトリメソッドによって、行分割テンソルが整形されており、値の数と一貫しているというアサーションが追加されます。入力の整形と一貫性を保証できる場合は、`validate=False` パラメータを使用してこれらの検査を省略できます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YQAOsT1_-PVg"
      },
      "source": [
        "### 不規則なテンソルに格納できるもの\n",
        "\n",
        "通常の `Tensor` と同様に、`RaggedTensor` の値にはすべて同じ型が必要で、値にはすべて同じネストの深度（テンソルの*階数*）が必要です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SqbPBd_w-PVi"
      },
      "outputs": [],
      "source": [
        "print(tf.ragged.constant([[\"Hi\"], [\"How\", \"are\", \"you\"]]))  # ok: type=string, rank=2"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "83ZCSJnQAWAf"
      },
      "outputs": [],
      "source": [
        "print(tf.ragged.constant([[[1, 2], [3]], [[4, 5]]]))        # ok: type=int32, rank=3"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ewA3cISdDfmP"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  tf.ragged.constant([[\"one\", \"two\"], [3, 4]])              # bad: multiple types\n",
        "except ValueError as exception:\n",
        "  print(exception)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EOWIlVidDl-n"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  tf.ragged.constant([\"A\", [\"B\", \"C\"]])                     # bad: multiple nesting depths\n",
        "except ValueError as exception:\n",
        "  print(exception)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nhHMFhSp-PVq"
      },
      "source": [
        "## 使用事例\n",
        "\n",
        "次の例は、各文の最初と最後に特殊マーカーを使用して、可変長クエリのバッチのユニグラムとバイグラムの埋め込みを構築して組み合わせるために `RaggedTensor` を使用する方法を示しています。この例で使用されている演算に関する詳細は、`tf.ragged` パッケージドキュメントをご覧ください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZBs_V7e--PVr"
      },
      "outputs": [],
      "source": [
        "queries = tf.ragged.constant([['Who', 'is', 'Dan', 'Smith'],\n",
        "                              ['Pause'],\n",
        "                              ['Will', 'it', 'rain', 'later', 'today']])\n",
        "\n",
        "# Create an embedding table.\n",
        "num_buckets = 1024\n",
        "embedding_size = 4\n",
        "embedding_table = tf.Variable(\n",
        "    tf.random.truncated_normal([num_buckets, embedding_size],\n",
        "                       stddev=1.0 / math.sqrt(embedding_size)))\n",
        "\n",
        "# Look up the embedding for each word.\n",
        "word_buckets = tf.strings.to_hash_bucket_fast(queries, num_buckets)\n",
        "word_embeddings = tf.nn.embedding_lookup(embedding_table, word_buckets)     # ①\n",
        "\n",
        "# Add markers to the beginning and end of each sentence.\n",
        "marker = tf.fill([queries.nrows(), 1], '#')\n",
        "padded = tf.concat([marker, queries, marker], axis=1)                       # ②\n",
        "\n",
        "# Build word bigrams &amp; look up embeddings.\n",
        "bigrams = tf.strings.join([padded[:, :-1], padded[:, 1:]], separator='+')   # ③\n",
        "\n",
        "bigram_buckets = tf.strings.to_hash_bucket_fast(bigrams, num_buckets)\n",
        "bigram_embeddings = tf.nn.embedding_lookup(embedding_table, bigram_buckets) # ④\n",
        "\n",
        "# Find the average embedding for each sentence\n",
        "all_embeddings = tf.concat([word_embeddings, bigram_embeddings], axis=1)    # ⑤\n",
        "avg_embedding = tf.reduce_mean(all_embeddings, axis=1)                      # ⑥\n",
        "print(avg_embedding)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Y_lE_LAVcWQH"
      },
      "source": [
        "![ragged_example](https://www.tensorflow.org/images/ragged_tensors/ragged_example.png)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "An_k0pX1-PVt"
      },
      "source": [
        "## 不規則な次元と一様次元\n",
        "\n",
        "***不規則な次元***とは、スライスの長さが異なる次元を指します。たとえば、内側の（列の）次元 `rt=[[3, 1, 4, 1], [], [5, 9, 2], [6], []]` は不規則です。これは、列スライス（`rt[0, :]`, ..., `rt[4, :]`）の長さが異なるからです。スライスの長さがすべて同一の次元は*一様次元*と呼ばれます。\n",
        "\n",
        "不規則なテンソルの一番外側の次元は、必ず一様です。これは、単一のスライスで構成されているためです（単一であるため長さが異なることがない）。残りの次元は、不規則または一様です。たとえば、形状 `[num_sentences, (num_words), embedding_size]`（`(num_words)` の括弧は次元が不規則であることを示す）の不規則な次元を使用して、文のバッチの各語に対する語の埋め込みを格納することができます。\n",
        "\n",
        "![sent_word_embed](https://www.tensorflow.org/images/ragged_tensors/sent_word_embed.png)\n",
        "\n",
        "不規則なテンソルは、複数の不規則な次元を持つことができます。たとえば、形状 `[num_documents, (num_paragraphs), (num_sentences), (num_words)]`（括弧は不規則な次元であることを示す）のテンソルを使用して、構造化テキストドキュメントのバッチを格納できます。\n",
        "\n",
        "`tf.Tensor` と同様に、不規則なテンソルの***階数***は、次元の合計数（不規則な次元と一様次元の両方を含む）です。***潜在的に不規則なテンソル*** は、`tf.Tensor` または `tf.RaggedTensor` のいずれかである値です。\n",
        "\n",
        "RaggedTensor の形状を記述する場合、不規則な次元は従来、それを丸括弧で囲むことで示されます。たとえば、上記の例で示したように、文のバッチの各語に対する語の埋め込みを格納する 3-D RaggedTensor の形は、`[num_sentences, (num_words), embedding_size]` と記述できます。\n",
        "\n",
        "`RaggedTensor.shape` 属性は、不規則なテンソルの `tf.TensorShape` を返します。不規則な次元のサイズは `None` です。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "M2Wzx4JEIvmb"
      },
      "outputs": [],
      "source": [
        "tf.ragged.constant([[\"Hi\"], [\"How\", \"are\", \"you\"]]).shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "G9tfJOeFlijE"
      },
      "source": [
        "メソッド `tf.RaggedTensor.bounding_shape` は、指定された `RaggedTensor` の密着する境界形状を見つけるために使用できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5DHaqXHxlWi0"
      },
      "outputs": [],
      "source": [
        "print(tf.ragged.constant([[\"Hi\"], [\"How\", \"are\", \"you\"]]).bounding_shape())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "V8e7x95UcLS6"
      },
      "source": [
        "## 不規則とスパース\n",
        "\n",
        "不規則なテンソルをスパーステンソルの一種として*捉えてはいけません*。具体的には、スパーステンソルは、*tf.Tensor の効率的なエンコーディング*で、同一のデータをコンパクトな形式でモデルする一方、不規則なテンソルは *tf.Tensor の拡張*で、データの拡張クラスをモデルします。演算を定義する場合に、この違いが非常に重要となります。\n",
        "\n",
        "- スパーステンソルまたは密なテンソルに演算を適用すると、同じ結果を得られます。\n",
        "- 不規則なテンソルまたはスパーステンソルに演算を適用すると、異なる結果が得られる場合があります。\n",
        "\n",
        "実例として、`concat`、`stack`、および `tile` などの配列演算が不規則なテンソルとスパーステンソルにどのように定義されているか考察してみましょう。不規則なテンソルを連結すると、各行が結合されて、長さが組み合わされた単一の行が形成されます。\n",
        "\n",
        "![ragged_concat](https://www.tensorflow.org/images/ragged_tensors/ragged_concat.png)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ush7IGUWLXIn"
      },
      "outputs": [],
      "source": [
        "ragged_x = tf.ragged.constant([[\"John\"], [\"a\", \"big\", \"dog\"], [\"my\", \"cat\"]])\n",
        "ragged_y = tf.ragged.constant([[\"fell\", \"asleep\"], [\"barked\"], [\"is\", \"fuzzy\"]])\n",
        "print(tf.concat([ragged_x, ragged_y], axis=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pvQzZG8zMoWa"
      },
      "source": [
        "ところが、次の例で説明されているように、スパーステンソルを連結すると、対応する密のテンソルと同じ結果が得られます（Ø は欠落した値）。\n",
        "\n",
        "![sparse_concat](https://www.tensorflow.org/images/ragged_tensors/sparse_concat.png)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eTIhGayQL0gI"
      },
      "outputs": [],
      "source": [
        "sparse_x = ragged_x.to_sparse()\n",
        "sparse_y = ragged_y.to_sparse()\n",
        "sparse_result = tf.sparse.concat(sp_inputs=[sparse_x, sparse_y], axis=1)\n",
        "print(tf.sparse.to_dense(sparse_result, ''))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Vl8eQN8pMuYx"
      },
      "source": [
        "この区別が重要であることを示すもう 1 つの例として、`tf.reduce_mean` などの演算の「各行の平均値」の定義を考察してみましょう。不規則なテンソルでは、行の平均値は、行の値の合計を行の幅で除算した値となりますが、スパーステンソルでは、行の平均値は、行の値の合計をスパーステンソルの全体的な幅（最長行の幅以上になる）で除算した値となります。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "u4yjxcK7IPXc"
      },
      "source": [
        "## TensorFlow API"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VoZGwFQjIYU5"
      },
      "source": [
        "### Keras\n",
        "\n",
        "[tf.keras](https://www.tensorflow.org/guide/keras) は、ディープラーニングモデルの構築とトレーニングに使用する TensorFlow の高位 API です。不規則なテンソルは、`tf.keras.Input` または `tf.keras.layers.InputLayer` に `ragged=True` を設定することで、Keras モデルに入力として渡すことができます。また、Keras レイヤー間で渡して、Keras モデルによって返すことも可能です。次の例は、不規則なテンソルを使用してトレーニングされる LSTM トイモデルを示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pHls7hQVJlk5"
      },
      "outputs": [],
      "source": [
        "# Task: predict whether each sentence is a question or not.\n",
        "sentences = tf.constant(\n",
        "    ['What makes you think she is a witch?',\n",
        "     'She turned me into a newt.',\n",
        "     'A newt?',\n",
        "     'Well, I got better.'])\n",
        "is_question = tf.constant([True, False, True, False])\n",
        "\n",
        "# Preprocess the input strings.\n",
        "hash_buckets = 1000\n",
        "words = tf.strings.split(sentences, ' ')\n",
        "hashed_words = tf.strings.to_hash_bucket_fast(words, hash_buckets)\n",
        "\n",
        "# Build the Keras model.\n",
        "keras_model = tf.keras.Sequential([\n",
        "    tf.keras.layers.Input(shape=[None], dtype=tf.int64, ragged=True),\n",
        "    tf.keras.layers.Embedding(hash_buckets, 16),\n",
        "    tf.keras.layers.LSTM(32, use_bias=False),\n",
        "    tf.keras.layers.Dense(32),\n",
        "    tf.keras.layers.Activation(tf.nn.relu),\n",
        "    tf.keras.layers.Dense(1)\n",
        "])\n",
        "\n",
        "keras_model.compile(loss='binary_crossentropy', optimizer='rmsprop')\n",
        "keras_model.fit(hashed_words, is_question, epochs=5)\n",
        "print(keras_model.predict(hashed_words))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8B_sdlt6Ij61"
      },
      "source": [
        "### tf.Example\n",
        "\n",
        "[tf.Example](https://www.tensorflow.org/tutorials/load_data/tfrecord) は、TensorFlow データの標準的な [protobuf](https://developers.google.com/protocol-buffers/) エンコーディングです。`tf.Example` でエンコードされるデータには通常、可変長の特徴量が含まれます。たとえば、次のコードは、異なる特徴の長さを持つ 4 つの `tf.Example` メッセージのバッチを定義しています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xsiglYM7TXGr"
      },
      "outputs": [],
      "source": [
        "import google.protobuf.text_format as pbtext\n",
        "\n",
        "def build_tf_example(s):\n",
        "  return pbtext.Merge(s, tf.train.Example()).SerializeToString()\n",
        "\n",
        "example_batch = [\n",
        "  build_tf_example(r'''\n",
        "    features {\n",
        "      feature {key: \"colors\" value {bytes_list {value: [\"red\", \"blue\"]} } }\n",
        "      feature {key: \"lengths\" value {int64_list {value: [7]} } } }'''),\n",
        "  build_tf_example(r'''\n",
        "    features {\n",
        "      feature {key: \"colors\" value {bytes_list {value: [\"orange\"]} } }\n",
        "      feature {key: \"lengths\" value {int64_list {value: []} } } }'''),\n",
        "  build_tf_example(r'''\n",
        "    features {\n",
        "      feature {key: \"colors\" value {bytes_list {value: [\"black\", \"yellow\"]} } }\n",
        "      feature {key: \"lengths\" value {int64_list {value: [1, 3]} } } }'''),\n",
        "  build_tf_example(r'''\n",
        "    features {\n",
        "      feature {key: \"colors\" value {bytes_list {value: [\"green\"]} } }\n",
        "      feature {key: \"lengths\" value {int64_list {value: [3, 5, 2]} } } }''')]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "szUuXFvtUL2o"
      },
      "source": [
        "このエンコードされたデータは、`tf.io.parse_example` を使用して解析できます。シリアル化文字列のテンソルと特徴の仕様を表すディクショナリを取り、ディクショナリのマッピング特徴名をテンソルに返します。不規則なテンソルに可変長の特徴を読み取るには、特徴仕様ディクショナリで `tf.io.RaggedFeature` を使用します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xcdaIbYVT4mo"
      },
      "outputs": [],
      "source": [
        "feature_specification = {\n",
        "    'colors': tf.io.RaggedFeature(tf.string),\n",
        "    'lengths': tf.io.RaggedFeature(tf.int64),\n",
        "}\n",
        "feature_tensors = tf.io.parse_example(example_batch, feature_specification)\n",
        "for name, value in feature_tensors.items():\n",
        "  print(\"{}={}\".format(name, value))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IK9X_8rXVr8h"
      },
      "source": [
        "複数の不規則な次元を持つ特徴を読み取るには、`tf.io.RaggedFeature` も使用できます。詳細については、[API ドキュメント](https://www.tensorflow.org/api_docs/python/tf/io/RaggedFeature)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UJowRhlxIX0R"
      },
      "source": [
        "### データセット\n",
        "\n",
        "[tf.data](https://www.tensorflow.org/guide/data) は、単純で再利用可能なピースから複雑な入力パイプラインを構築できる API です。そのコアデータ構造は `tf.data.Dataset` で、一連の要素を表し、その各要素には 1 つ以上のコンポーネントが含まれます。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "fBml1m2G2vO9"
      },
      "outputs": [],
      "source": [
        "# Helper function used to print datasets in the examples below.\n",
        "def print_dictionary_dataset(dataset):\n",
        "  for i, element in enumerate(dataset):\n",
        "    print(\"Element {}:\".format(i))\n",
        "    for (feature_name, feature_value) in element.items():\n",
        "      print('{:&gt;14} = {}'.format(feature_name, feature_value))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gEu_H1Sp2jz1"
      },
      "source": [
        "#### 不規則なテンソルを伴うデータセットを構築する\n",
        "\n",
        "データセットは、`tf.Tensor` または numpy の `array` から構築する際に使用されるのと同じメソッド、つまり `Dataset.from_tensor_slices` を使用して、不規則なテンソルから構築できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BuelF_y2mEq9"
      },
      "outputs": [],
      "source": [
        "dataset = tf.data.Dataset.from_tensor_slices(feature_tensors)\n",
        "print_dictionary_dataset(dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mC-QNkJc56De"
      },
      "source": [
        "注意: `Dataset.from_generator` は不規則なテンソルにまだ対応していませんが、近々サポートされる予定です。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K0UKvBLf1VMu"
      },
      "source": [
        "#### 不規則なテンソルを伴うデータセットをバッチ処理またはバッチ解除する\n",
        "\n",
        "不規則なテンソルを伴うデータセットは、`Dataset.batch` メソッドを使用してバッチ処理できます（*n* 個の連続した要素を単一の要素に組み合わせます）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lk62aRz63IZn"
      },
      "outputs": [],
      "source": [
        "batched_dataset = dataset.batch(2)\n",
        "print_dictionary_dataset(batched_dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NLSGiYEQ5A8N"
      },
      "source": [
        "逆に、バッチ処理されたデータセットは、`Dataset.unbatch` を使用して、フラットデータセットに変換できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CxLlaPw_5Je4"
      },
      "outputs": [],
      "source": [
        "unbatched_dataset = batched_dataset.unbatch()\n",
        "print_dictionary_dataset(unbatched_dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YzpLQFh33q0N"
      },
      "source": [
        "#### 可変長の非不規則テンソルを伴うデータセットをバッチ処理する\n",
        "\n",
        "非不規則テンソルを含むデータセットで、テンソルの長さが要素間で異なる場合は、`dense_to_ragged_batch` 変換を使用して、それらの非不規則テンソルを不規則なテンソルにバッチ処理できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PYnhERwh3_mf"
      },
      "outputs": [],
      "source": [
        "non_ragged_dataset = tf.data.Dataset.from_tensor_slices([1, 5, 3, 2, 8])\n",
        "non_ragged_dataset = non_ragged_dataset.map(tf.range)\n",
        "batched_non_ragged_dataset = non_ragged_dataset.apply(\n",
        "    tf.data.experimental.dense_to_ragged_batch(2))\n",
        "for element in batched_non_ragged_dataset:\n",
        "  print(element)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nXFPeE-CzJ-s"
      },
      "source": [
        "#### 不規則なテンソルを伴うデータセットを変換する\n",
        "\n",
        "データセット内の不規則なテンソルは、`Dataset.map` を使用して作成または変換することもできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Ios1GuG-pf9U"
      },
      "outputs": [],
      "source": [
        "def transform_lengths(features):\n",
        "  return {\n",
        "      'mean_length': tf.math.reduce_mean(features['lengths']),\n",
        "      'length_ranges': tf.ragged.range(features['lengths'])}\n",
        "transformed_dataset = dataset.map(transform_lengths)\n",
        "print_dictionary_dataset(transformed_dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WD2lWw3fIXrg"
      },
      "source": [
        "### tf.function\n",
        "\n",
        "[tf.function](https://www.tensorflow.org/guide/function) は、Python 関数向けに TensorFlow グラフを事前計算するデコレータで、TensorFlow コードのパフォーマンスを大幅に改善できます。不規則なテンソルは、`@tf.function` でデコレートされた関数と透過的に使用することができます。たとえば、次の関数は、不規則なテンソルと非不規則テンソルの両方で動作します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PfyxgVaj_8tl"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def make_palindrome(x, axis):\n",
        "  return tf.concat([x, tf.reverse(x, [axis])], axis)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vcZdzvEnDEt0"
      },
      "outputs": [],
      "source": [
        "make_palindrome(tf.constant([[1, 2], [3, 4], [5, 6]]), axis=1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4WfCMIgdDMxj"
      },
      "outputs": [],
      "source": [
        "make_palindrome(tf.ragged.constant([[1, 2], [3], [4, 5, 6]]), axis=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "X2p69YPOBUz8"
      },
      "source": [
        "`tf.function` で `input_signature` を明示的に指定する場合は、`tf.RaggedTensorSpec` を使用して行います。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "k6-hkhdDBk6G"
      },
      "outputs": [],
      "source": [
        "@tf.function(\n",
        "    input_signature=[tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int32)])\n",
        "def max_and_min(rt):\n",
        "  return (tf.math.reduce_max(rt, axis=-1), tf.math.reduce_min(rt, axis=-1))\n",
        "\n",
        "max_and_min(tf.ragged.constant([[1, 2], [3], [4, 5, 6]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fSs-7E0VD85q"
      },
      "source": [
        "#### 具象関数\n",
        "\n",
        "[具象関数](https://www.tensorflow.org/guide/concrete_function)は、`tf.function` で構築された個別のトレース済みグラフをカプセル化します。TensorFlow 2.3（および `tf-nightly`）より、不規則なテンソルは、具象関数と透過的に使用できます。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yyJeXJ4wFWox"
      },
      "outputs": [],
      "source": [
        "# Preferred way to use ragged tensors with concrete functions (TF 2.3+):\n",
        "try:\n",
        "  @tf.function\n",
        "  def increment(x):\n",
        "    return x + 1\n",
        "\n",
        "  rt = tf.ragged.constant([[1, 2], [3], [4, 5, 6]])\n",
        "  cf = increment.get_concrete_function(rt)\n",
        "  print(cf(rt))\n",
        "except Exception as e:\n",
        "  print(f\"Not supported before TF 2.3: {type(e)}: {e}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cXWDzG5jFVro"
      },
      "source": [
        "TensorFlow 2.3 より前の具象関数で不規則なテンソルを使用する必要がある場合は、不規則なテンソルをコンポーネントに分解し（`values` および `row_splits`）、個別の引数として渡すことをお勧めします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5dYzeI0FIdDV"
      },
      "outputs": [],
      "source": [
        "# Backwards-compatible way to use ragged tensors with concrete functions:\n",
        "@tf.function\n",
        "def decomposed_ragged_increment(x_values, x_splits):\n",
        "  x = tf.RaggedTensor.from_row_splits(x_values, x_splits)\n",
        "  return x + 1\n",
        "\n",
        "rt = tf.ragged.constant([[1, 2], [3], [4, 5, 6]])\n",
        "cf = decomposed_ragged_increment.get_concrete_function(rt.values, rt.row_splits)\n",
        "print(cf(rt.values, rt.row_splits))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iYLyPlatIXhh"
      },
      "source": [
        "### SavedModel\n",
        "\n",
        "[SavedModel](https://www.tensorflow.org/guide/saved_model) は、シリアル化された TensorFlow プログラムで、重みと計算の両方が含まれます。Keras モデルまたはカスタムモデルから構築することができますが、いずれの場合でも、不規則なテンソルは SavedModel によって定義された関数とメソッドを使って、透過的に使用することができます。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "98VpBSdOgWqL"
      },
      "source": [
        "#### 例: Keras モデルを保存する"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "D-Dg9w7Je5pU"
      },
      "outputs": [],
      "source": [
        "import tempfile\n",
        "\n",
        "keras_module_path = tempfile.mkdtemp()\n",
        "tf.saved_model.save(keras_model, keras_module_path)\n",
        "imported_model = tf.saved_model.load(keras_module_path)\n",
        "imported_model(hashed_words)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9-7k-E92gaoR"
      },
      "source": [
        "#### 例: カスタムモデルを保存する\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Sfem1ESrdGzX"
      },
      "outputs": [],
      "source": [
        "class CustomModule(tf.Module):\n",
        "  def __init__(self, variable_value):\n",
        "    super(CustomModule, self).__init__()\n",
        "    self.v = tf.Variable(variable_value)\n",
        "\n",
        "  @tf.function\n",
        "  def grow(self, x):\n",
        "    return x * self.v\n",
        "\n",
        "module = CustomModule(100.0)\n",
        "\n",
        "# Before saving a custom model, we must ensure that concrete functions are\n",
        "# built for each input signature that we will need.\n",
        "module.grow.get_concrete_function(tf.RaggedTensorSpec(shape=[None, None],\n",
        "                                                      dtype=tf.float32))\n",
        "\n",
        "custom_module_path = tempfile.mkdtemp()\n",
        "tf.saved_model.save(module, custom_module_path)\n",
        "imported_model = tf.saved_model.load(custom_module_path)\n",
        "imported_model.grow(tf.ragged.constant([[1.0, 4.0, 3.0], [2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SAxis5KBhrBN"
      },
      "source": [
        "注意: SavedModel [シグネチャ](https://www.tensorflow.org/guide/saved_model#specifying_signatures_during_export)は具象関数です。上記の「具象関数」のセクションで説明したとおり、具象関数による不規則なテンソルの処理は、TensorFlow 2.3 以降（および `tf_nightly`）でのみ正しく行われます。TensorFlow の旧バージョンで SavedModel シグネチャを使用する必要がある場合は、不規則なテンソルをコンポーネントテンソルに分解することをお勧めします。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cRcHzS6pcHYC"
      },
      "source": [
        "## オーバーロードされた演算\n",
        "\n",
        "`RaggedTensor` クラスは標準的な Python 算術と比較演算をオーバーロードするため、基本的な要素ごとの計算が簡単に行えるようになります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "skScd37P-PVu"
      },
      "outputs": [],
      "source": [
        "x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]])\n",
        "y = tf.ragged.constant([[1, 1], [2], [3, 3, 3]])\n",
        "print(x + y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XEGgbZHV-PVw"
      },
      "source": [
        "オーバーロードされた演算は、要素ごとの計算を実行するため、すべての二項演算への入力が同じ形状であるか、同じ形状にブロードキャスト可能である必要があります。最も単純なブロードキャストの場合、単一のスカラーは、不規則なテンソルの各値と要素ごとに組み合わされます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IYybEEWc-PVx"
      },
      "outputs": [],
      "source": [
        "x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]])\n",
        "print(x + 3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "okGb9dIi-PVz"
      },
      "source": [
        "より高度な事例については、**ブロードキャスト**に関するセクションをご覧ください。\n",
        "\n",
        "不規則なテンソルは、通常の `Tensor` と同じ一連の演算子をオーバーロードします。それらは、単項演算子の `-`、`~`、および `abs()`、二項演算子の `+`、`-`、`*`、`/`、`//`、`%`、`**`、`&`、`|`、`^`、`==`、`<`、`<=`、`>`、および `>=` です。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "f2anbs6ZnFtl"
      },
      "source": [
        "## インデキシング\n",
        "\n",
        "不規則なテンソルは、多次元のインデキシングとスライシングを含む、Python 形式のインデキシングをサポートしています。次の例では、2-D と 3-D の不規則なテンソルによる不規則なテンソルのインデキシングを示しています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XuEwmC3t_ITL"
      },
      "source": [
        "### インデキシングの例: 2D 不規則テンソル"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MbSRZRDz-PV1"
      },
      "outputs": [],
      "source": [
        "queries = tf.ragged.constant(\n",
        "    [['Who', 'is', 'George', 'Washington'],\n",
        "     ['What', 'is', 'the', 'weather', 'tomorrow'],\n",
        "     ['Goodnight']])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2HRs2xhh-vZE"
      },
      "outputs": [],
      "source": [
        "print(queries[1])                   # A single query"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EFfjZV7YA3UH"
      },
      "outputs": [],
      "source": [
        "print(queries[1, 2])                # A single word"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VISRPQSdA3xn"
      },
      "outputs": [],
      "source": [
        "print(queries[1:])                  # Everything but the first row"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "J1PpSyKQBMng"
      },
      "outputs": [],
      "source": [
        "print(queries[:, :3])               # The first 3 words of each query"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ixrhHmJBeidy"
      },
      "outputs": [],
      "source": [
        "print(queries[:, -2:])              # The last 2 words of each query"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cnOP6Vza-PV4"
      },
      "source": [
        "### インデキシングの例: 3D 不規則テンソル"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8VbqbKcE-PV6"
      },
      "outputs": [],
      "source": [
        "rt = tf.ragged.constant([[[1, 2, 3], [4]],\n",
        "                         [[5], [], [6]],\n",
        "                         [[7]],\n",
        "                         [[8, 9], [10]]])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f9WPVWf4grVp"
      },
      "outputs": [],
      "source": [
        "print(rt[1])                        # Second row (2-D RaggedTensor)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ad8FGJoABjQH"
      },
      "outputs": [],
      "source": [
        "print(rt[3, 0])                     # First element of fourth row (1-D Tensor)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MPPr-a-bBjFE"
      },
      "outputs": [],
      "source": [
        "print(rt[:, 1:3])                   # Items 1-3 of each row (3-D RaggedTensor)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6SIDeoIUBi4z"
      },
      "outputs": [],
      "source": [
        "print(rt[:, -1:])                   # Last item of each row (3-D RaggedTensor)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_d3nBh1GnWvU"
      },
      "source": [
        "`RaggedTensor` は多次元のインデキシングとスライシングをサポートしていますが、1 つだけ制限があります。それは、不規則な次元へのインデキシングはできないということです。示された値が一部の行のみに存在することがあるため、これは悩ましいことです。このような場合は、（1）`IndexError` を発するべきか、（2）デフォルト値を使用すべきか、（3）その値をスキップして、開始した数より少ない行数のテンソルを返すべきか、明確ではありません。[Python の基本原則](https://www.python.org/dev/peps/pep-0020/)（「曖昧なものに出逢ったら、その意味を適当に推測してはいけない。」）に則り、現在のところこの演算を許可していません。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IsWKETULAJbN"
      },
      "source": [
        "## テンソル型の変換\n",
        "\n",
        "`RaggedTensor` クラスは、`RaggedTensor` と `tf.Tensor` または `tf.SparseTensors` 間の変換に使用できるメソッドを定義します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "INnfmZGcBoU_"
      },
      "outputs": [],
      "source": [
        "ragged_sentences = tf.ragged.constant([\n",
        "    ['Hi'], ['Welcome', 'to', 'the', 'fair'], ['Have', 'fun']])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "__iJ4iXtkGOx"
      },
      "outputs": [],
      "source": [
        "# RaggedTensor -&gt; Tensor\n",
        "print(ragged_sentences.to_tensor(default_value='', shape=[None, 10]))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-rfiyYqne8QN"
      },
      "outputs": [],
      "source": [
        "# Tensor -&gt; RaggedTensor\n",
        "x = [[1, 3, -1, -1], [2, -1, -1, -1], [4, 5, 8, 9]]\n",
        "print(tf.RaggedTensor.from_tensor(x, padding=-1))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "41WAZLXNnbwH"
      },
      "outputs": [],
      "source": [
        "#RaggedTensor -&gt; SparseTensor\n",
        "print(ragged_sentences.to_sparse())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "S8MkYo2hfVhj"
      },
      "outputs": [],
      "source": [
        "# SparseTensor -&gt; RaggedTensor\n",
        "st = tf.SparseTensor(indices=[[0, 0], [2, 0], [2, 1]],\n",
        "                     values=['a', 'b', 'c'],\n",
        "                     dense_shape=[3, 3])\n",
        "print(tf.RaggedTensor.from_sparse(st))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qx025sNMkAHH"
      },
      "source": [
        "## 不規則なテンソルの評価\n",
        "\n",
        "不規則なテンソルの値にアクセスするには、次を行えます。\n",
        "\n",
        "1. `tf.RaggedTensor.to_list()` を使用して、不規則なテンソルをネストされた Python リストに変換する。\n",
        "2. `tf.RaggedTensor.numpy()` を使用して、不規則なテンソルを numpy 配列にネストされた値を持つ numpy 配列に変換する。\n",
        "3. `tf.RaggedTensor.values` と `tf.RaggedTensor.row_splits` プロパティ、または `tf.RaggedTensor.row_lengths()` や `tf.RaggedTensor.value_rowids()` などの行分割メソッドを使用して、不規則なテンソルをコンポーネントに分解する。\n",
        "4. Python インデキシングを使用して、不規則なテンソルから値を選択する。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uMm1WMkc-PV_"
      },
      "outputs": [],
      "source": [
        "rt = tf.ragged.constant([[1, 2], [3, 4, 5], [6], [], [7]])\n",
        "print(\"python list:\", rt.to_list())\n",
        "print(\"numpy array:\", rt.numpy())\n",
        "print(\"values:\", rt.values.numpy())\n",
        "print(\"splits:\", rt.row_splits.numpy())\n",
        "print(\"indexed value:\", rt[1].numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EdljbNPq-PWS"
      },
      "source": [
        "## ブロードキャスト\n",
        "\n",
        "ブロードキャストとは、さまざまな形状のテンソルに要素単位の演算で互換性のある形状を持たせるプロセスです。ブロードキャストの詳しい背景については、次をご覧ください。\n",
        "\n",
        "- [Numpy: ブロードキャスト](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)\n",
        "- `tf.broadcast_dynamic_shape`\n",
        "- `tf.broadcast_to`\n",
        "\n",
        "次は、互換性のある形状を持たせるために `x` と `y` の 2 つの入力をブロードキャストする基本手順です。\n",
        "\n",
        "1. `x` と `y` の次元数が異なる場合は、次元数が同じになるまで外側の次元（サイズ 1）を追加します。\n",
        "\n",
        "2. `x` と `y` のサイズが異なる各次元に対し、次のことを行います。\n",
        "\n",
        "    - `x` または `y` の次元 `d` がサイズ `1` の場合は、もう片方の入力のサイズに一致するように、次元 `d` でその値を繰り返します。\n",
        "\n",
        "    - そうでない場合は、例外（`x` と `y` はブロードキャスト互換ではない）を発します。\n",
        "\n",
        "一様次元内のテンソルのサイズは単一の数字であり（その次元のスライスのサイズ）、不規則な次元内のテンソルサイズはスライスの長さのリスト（その次元のすべてのスライス）です。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-S2hOUWx-PWU"
      },
      "source": [
        "### ブロードキャストの例"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0n095XdR-PWU"
      },
      "outputs": [],
      "source": [
        "# x       (2D ragged):  2 x (num_rows)\n",
        "# y       (scalar)\n",
        "# result  (2D ragged):  2 x (num_rows)\n",
        "x = tf.ragged.constant([[1, 2], [3]])\n",
        "y = 3\n",
        "print(x + y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0SVYk5AP-PWW"
      },
      "outputs": [],
      "source": [
        "# x         (2d ragged):  3 x (num_rows)\n",
        "# y         (2d tensor):  3 x          1\n",
        "# Result    (2d ragged):  3 x (num_rows)\n",
        "x = tf.ragged.constant(\n",
        "   [[10, 87, 12],\n",
        "    [19, 53],\n",
        "    [12, 32]])\n",
        "y = [[1000], [2000], [3000]]\n",
        "print(x + y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MsfBMD80s8Ux"
      },
      "outputs": [],
      "source": [
        "# x      (3d ragged):  2 x (r1) x 2\n",
        "# y      (2d ragged):         1 x 1\n",
        "# Result (3d ragged):  2 x (r1) x 2\n",
        "x = tf.ragged.constant(\n",
        "    [[[1, 2], [3, 4], [5, 6]],\n",
        "     [[7, 8]]],\n",
        "    ragged_rank=1)\n",
        "y = tf.constant([[10]])\n",
        "print(x + y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rEj5QVfnva0t"
      },
      "outputs": [],
      "source": [
        "# x      (3d ragged):  2 x (r1) x (r2) x 1\n",
        "# y      (1d tensor):                    3\n",
        "# Result (3d ragged):  2 x (r1) x (r2) x 3\n",
        "x = tf.ragged.constant(\n",
        "    [\n",
        "        [\n",
        "            [[1], [2]],\n",
        "            [],\n",
        "            [[3]],\n",
        "            [[4]],\n",
        "        ],\n",
        "        [\n",
        "            [[5], [6]],\n",
        "            [[7]]\n",
        "        ]\n",
        "    ],\n",
        "    ragged_rank=2)\n",
        "y = tf.constant([10, 20, 30])\n",
        "print(x + y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uennZ64Aqftb"
      },
      "source": [
        "次は、ブロードキャストしない形状の例です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UpI0FlfL4Eim"
      },
      "outputs": [],
      "source": [
        "# x      (2d ragged): 3 x (r1)\n",
        "# y      (2d tensor): 3 x    4  # trailing dimensions do not match\n",
        "x = tf.ragged.constant([[1, 2], [3, 4, 5, 6], [7]])\n",
        "y = tf.constant([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])\n",
        "try:\n",
        "  x + y\n",
        "except tf.errors.InvalidArgumentError as exception:\n",
        "  print(exception)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qGq1zOT4zMoc"
      },
      "outputs": [],
      "source": [
        "# x      (2d ragged): 3 x (r1)\n",
        "# y      (2d ragged): 3 x (r2)  # ragged dimensions do not match.\n",
        "x = tf.ragged.constant([[1, 2, 3], [4], [5, 6]])\n",
        "y = tf.ragged.constant([[10, 20], [30, 40], [50]])\n",
        "try:\n",
        "  x + y\n",
        "except tf.errors.InvalidArgumentError as exception:\n",
        "  print(exception)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CvLae5vMqeji"
      },
      "outputs": [],
      "source": [
        "# x      (3d ragged): 3 x (r1) x 2\n",
        "# y      (3d ragged): 3 x (r1) x 3  # trailing dimensions do not match\n",
        "x = tf.ragged.constant([[[1, 2], [3, 4], [5, 6]],\n",
        "                        [[7, 8], [9, 10]]])\n",
        "y = tf.ragged.constant([[[1, 2, 0], [3, 4, 0], [5, 6, 0]],\n",
        "                        [[7, 8, 0], [9, 10, 0]]])\n",
        "try:\n",
        "  x + y\n",
        "except tf.errors.InvalidArgumentError as exception:\n",
        "  print(exception)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m0wQkLfV-PWa"
      },
      "source": [
        "## RaggedTensor エンコーディング\n",
        "\n",
        "不規則なテンソルは、`RaggedTensor` クラスを使ってエンコードされています。内部的に、各 `RaggedTensor` は次の項目で構成されています。\n",
        "\n",
        "- `values` テンソル。可変長の行をフラットリストに連結します。\n",
        "- `row_partition`。フラット化された値がどのように行に分割されているかを示します。\n",
        "\n",
        "![ragged_encoding_2](https://www.tensorflow.org/images/ragged_tensors/ragged_encoding_2.png)\n",
        "\n",
        "`row_partition` は、次の 4 つのエンコーディングで保存できます。\n",
        "\n",
        "- `row_splits` は、行間の分割ポイントを指定する整数ベクトルです。\n",
        "- `value_rowids` は、各値の行インデックスを指定する整数ベクトルです。\n",
        "- `row_lengths` は、各行の長さを指定する整数ベクトルです。\n",
        "- `uniform_row_length` は、すべての行の単一の長さを指定する整数スカラーです。\n",
        "\n",
        "![partition_encodings](https://www.tensorflow.org/images/ragged_tensors/partition_encodings.png)\n",
        "\n",
        "整数スカラー `nrows` は、`valuerowids ` のある空の後続の行、または ` uniform_row_length` のある空の行を構成するように、`row_partition` エンコーディングに含めることができます。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MrLgMu0gPuo-"
      },
      "outputs": [],
      "source": [
        "rt = tf.RaggedTensor.from_row_splits(\n",
        "    values=[3, 1, 4, 1, 5, 9, 2],\n",
        "    row_splits=[0, 4, 4, 6, 7])\n",
        "print(rt)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wEfZOKwN1Ra_"
      },
      "source": [
        "行分割に使用するエンコーディングの選択は、不規則なテンソルによって内部的に管理されており、一部の文脈で効率性を改善することができます。具体的には、異なる行分割スキームのメリットと欠点には次のようなものがあります。\n",
        "\n",
        "- **効率的なインデキシング**: `row_splits` エンコーディングにより、不規則なテンソルへの一定時間のインデキシングとスライシングが可能になります。\n",
        "\n",
        "- **効率的な連結**: `row_lengths` エンコーディングは、不規則なテンソルを連結する場合により効率性が高くなります。2 つのテンソルを連結する際に、行の長さが変化しないためです。\n",
        "\n",
        "- **小さなエンコーディングサイズ**: `value_rowids` エンコーディングは、空の行が大量にある不規則なテンソルを格納する際により効率性が高くなります。テンソルのサイズが、値の合計数にのみ依存しているためです。一方、`row_splits` と `row_lengths` エンコーディングは、より長い行のある不規則なテンソルを格納する際により効率性が高くなります。各行に必要なスカラー値が 1 つのみであるためです。\n",
        "\n",
        "- **互換性**: `value_rowids` スキームは、`tf.segment_sum` などの演算で使用される[セグメンテーション](https://www.tensorflow.org/api_docs/python/tf/math#about_segmentation)形式に一致しています。`row_limits` スキームは、`tf.sequence_mask` 演算で使用される形式に一致しています。\n",
        "\n",
        "- **一様次元**: 以下で説明するように、一様次元を伴う不規則なテンソルのエンコードには、`uniform_row_length` エンコーディングが使用されます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bpB7xKoUPtU6"
      },
      "source": [
        "### 複数の不規則な次元\n",
        "\n",
        "複数の不規則な次元を伴う不規則なテンソルは、`values` テンソルにネストされた `RaggedTensor` を使用することで、エンコードされます。それぞれのネストされた `RaggedTensor` は、単一の不規則な次元を追加します。\n",
        "\n",
        "![ragged_rank_2](https://www.tensorflow.org/images/ragged_tensors/ragged_rank_2.png)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yy3IGT2a-PWb"
      },
      "outputs": [],
      "source": [
        "rt = tf.RaggedTensor.from_row_splits(\n",
        "    values=tf.RaggedTensor.from_row_splits(\n",
        "        values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],\n",
        "        row_splits=[0, 3, 3, 5, 9, 10]),\n",
        "    row_splits=[0, 1, 1, 5])\n",
        "print(rt)\n",
        "print(\"Shape: {}\".format(rt.shape))\n",
        "print(\"Number of partitioned dimensions: {}\".format(rt.ragged_rank))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5HqEEDzk-PWc"
      },
      "source": [
        "ファクトリ関数 `tf.RaggedTensor.from_nested_row_splits` は、`row_splits` テンソルのリストを提供することで、複数の不規則な次元を持つ RaggedTensor を直接構築するために使用できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "AKYhtFcT-PWd"
      },
      "outputs": [],
      "source": [
        "rt = tf.RaggedTensor.from_nested_row_splits(\n",
        "    flat_values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],\n",
        "    nested_row_splits=([0, 1, 1, 5], [0, 3, 3, 5, 9, 10]))\n",
        "print(rt)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BqAfbkAC56m0"
      },
      "source": [
        "### 不規則な階数とフラット値\n",
        "\n",
        "不規則なテンソルの***不規則な階数***は、Tensor が持つ基底の `values` が何回分割されたかを表す数字です（`RaggedTensor`  オブジェクトのネスト深度）。最も内側の `values` テンソルは ***flat_values*** と呼ばれます。次の例では、`conversations` には ragged_rank=3 があり、`flat_values` は 24 個の文字列を持つ 1D `Tensor` です。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BXp-Tt2bClem"
      },
      "outputs": [],
      "source": [
        "# shape = [batch, (paragraph), (sentence), (word)]\n",
        "conversations = tf.ragged.constant(\n",
        "    [[[[\"I\", \"like\", \"ragged\", \"tensors.\"]],\n",
        "      [[\"Oh\", \"yeah?\"], [\"What\", \"can\", \"you\", \"use\", \"them\", \"for?\"]],\n",
        "      [[\"Processing\", \"variable\", \"length\", \"data!\"]]],\n",
        "     [[[\"I\", \"like\", \"cheese.\"], [\"Do\", \"you?\"]],\n",
        "      [[\"Yes.\"], [\"I\", \"do.\"]]]])\n",
        "conversations.shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DZUMrgxXFd5s"
      },
      "outputs": [],
      "source": [
        "assert conversations.ragged_rank == len(conversations.nested_row_splits)\n",
        "conversations.ragged_rank  # Number of partitioned dimensions."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xXLSNpS0Fdvp"
      },
      "outputs": [],
      "source": [
        "conversations.flat_values.numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uba2EnAY-PWf"
      },
      "source": [
        "### 一様の内部次元\n",
        "\n",
        "一様の内部次元を伴う不規則なテンソルは、flat_values（最内部の `values`）に多次元の `tf.Tensor` を使用することでエンコードされます。\n",
        "\n",
        "![uniform_inner](https://www.tensorflow.org/images/ragged_tensors/uniform_inner.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "z2sHwHdy-PWg"
      },
      "outputs": [],
      "source": [
        "rt = tf.RaggedTensor.from_row_splits(\n",
        "    values=[[1, 3], [0, 0], [1, 3], [5, 3], [3, 3], [1, 2]],\n",
        "    row_splits=[0, 3, 4, 6])\n",
        "print(rt)\n",
        "print(\"Shape: {}\".format(rt.shape))\n",
        "print(\"Number of partitioned dimensions: {}\".format(rt.ragged_rank))\n",
        "print(\"Flat values shape: {}\".format(rt.flat_values.shape))\n",
        "print(\"Flat values:\\n{}\".format(rt.flat_values))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WoGRKd50x_qz"
      },
      "source": [
        "### 一様の非内部次元\n",
        "\n",
        "一様の非内部次元を伴う不規則なテンソルは、`uniform_row_length` で行を分割してエンコードします。\n",
        "\n",
        "![uniform_outer](https://www.tensorflow.org/images/ragged_tensors/uniform_outer.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "70q1aCKwySgS"
      },
      "outputs": [],
      "source": [
        "rt = tf.RaggedTensor.from_uniform_row_length(\n",
        "    values=tf.RaggedTensor.from_row_splits(\n",
        "        values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],\n",
        "        row_splits=[0, 3, 5, 9, 10]),\n",
        "    uniform_row_length=2)\n",
        "print(rt)\n",
        "print(\"Shape: {}\".format(rt.shape))\n",
        "print(\"Number of partitioned dimensions: {}\".format(rt.ragged_rank))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "ragged_tensor.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
